package rootpw;

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.Timer;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

final class AgentUI {
	protected static ComponentEventHandler ceh = new ComponentEventHandler();
	protected static String passphrase = null;
	TrayIcon trayIcon = null;

	// AgentUI constructor
	AgentUI(AgentListBuilder lb)
	{
		final JFrame frame = new JFrame("RootPW Agent"); // window title
		final JPanel panel = new JPanel(); 
		final AgentUIContent content = new AgentUIContent(lb.hosts, lb.passwords); // content JPanel
		final Image iconimage = Toolkit.getDefaultToolkit().createImage(KeyImage.jpegdata); 
		final PasswordDialog pd = new PasswordDialog(frame, this); //  modal pop-up password prompt
	    AgentUITimer timer;
        
		// layout & packing of app window
		panel.setLayout(new BorderLayout());
		panel.add(content);
		frame.setLayout(new BorderLayout());
		frame.setIconImage(iconimage);
		frame.getContentPane().add(panel);
		frame.pack();
		frame.setVisible(true);	
		
		// set a timer that prompts for password after inactivity
		timer = new AgentUITimer(pd);
		pd.setTimer(timer.timer);
		pd.setLocationRelativeTo(frame);
		pd.setSize(new Dimension(300,150));
		pd.setVisible(true);
		
		// set up a system tray icon if it's supported
		if (getSystray(iconimage, frame)) {
			frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
		} else {
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		}		
	}
	
	// tries to create a systray icon. if it can't, returns false,
	// otherwise returns true
	private boolean getSystray(Image image, final JFrame frame) {

        if (SystemTray.isSupported()) {	
			SystemTray tray = SystemTray.getSystemTray();
		    //Image image = Toolkit.getDefaultToolkit().getImage("tray.gif");
	
		    MouseListener mouseListener = new MouseListener() {
		                
		        public void mouseClicked(MouseEvent e) {
		        	if (e.getButton() == 1) {
		        		frame.setVisible(true);
		        	}
		            //System.out.println("Tray Icon - Mouse clicked!");                 
		        }
	
		        public void mouseEntered(MouseEvent e) {
		            //System.out.println("Tray Icon - Mouse entered!");                 
		        }
	
		        public void mouseExited(MouseEvent e) {
		            //System.out.println("Tray Icon - Mouse exited!");                 
		        }
	
		        public void mousePressed(MouseEvent e) {
		            //System.out.println("Tray Icon - Mouse pressed!");                 
		        }
	
		        public void mouseReleased(MouseEvent e) {
		            //System.out.println("Tray Icon - Mouse released!");                 
		        }
		    };
	
		    ActionListener exitListener = new ActionListener() {
		        public void actionPerformed(ActionEvent e) {
		            //System.out.println("Exiting...");
		            System.exit(0);
		        }
		    };
		    
		    ActionListener restoreListener = new ActionListener() {
		    	public void actionPerformed(ActionEvent e) {
		    		//System.out.println("Restoring...");
		    		frame.setVisible(true);
		    	}
		    };
		            
		    PopupMenu popup = new PopupMenu();
		    MenuItem exitItem = new MenuItem("Exit");
		    MenuItem restoreItem = new MenuItem("Restore");
		    exitItem.addActionListener(exitListener);
		    restoreItem.addActionListener(restoreListener);

		    popup.add(restoreItem);
		    popup.add(exitItem);

	
		    trayIcon = new TrayIcon(image, "RootPW Agent", popup);
	            
		    trayIcon.setImageAutoSize(true);
		    trayIcon.addMouseListener(mouseListener);
	
		    try {
		        tray.add(trayIcon);
		    } catch (AWTException e) {
		        System.out.println("TrayIcon could not be added.");
		    }

        } else {
        	return false;
        	//  System Tray is not supported
        }
    return true;
	}
	
	// timer which re-prompts for password after a period of inactivity
	class AgentUITimer implements ActionListener {
		PasswordDialog lpd;
		Timer timer;
		AgentUITimer(PasswordDialog pd) {
			super();
			lpd = pd;
			timer = new Timer(30 * 60 * 1000, this);
		}
				public void actionPerformed(ActionEvent e) {
					class ClipOwner implements ClipboardOwner
					{
						ClipOwner()
						{		
							Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
							StringSelection ss = new StringSelection("");
							clipboard.setContents(ss, this);
						}
						public void lostOwnership(Clipboard arg0,
								Transferable arg1) {
							// TODO Auto-generated method stub
							
						}
					}
					new ClipOwner(); // wipe the clipboard
					passphrase = null; // null the passphrase
					lpd.setVisible(true); // popup the password dialog
				}
	}
}

// content pane... contains the list 
final class AgentUIContent extends JPanel {
	private static final long serialVersionUID = 7984464388907193874L;
	private static Object[] pl = null;

	AgentUIContent(Object[] hosts, Object[] passwords)
	{
		JList 					list = new JList(hosts);
		JScrollPane 			listScrollPane = new JScrollPane(list);
		pl = passwords;
		
		// set layout of this JPanel, configure & pack list
		setLayout(new BoxLayout(this, 0));
		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		list.addListSelectionListener(new CopyToClipboard());
		list.setVisibleRowCount(hosts.length/4);
		list.setLayoutOrientation(JList.VERTICAL_WRAP);
		listScrollPane.setMinimumSize(new Dimension(250,100));
		listScrollPane.setPreferredSize(new Dimension(500,200));
		list.addComponentListener(AgentUI.ceh);
		
    	// listen for mouse clicks in the list & redo clipboard copy -
		// for situations when one makes a selection, leaves the window,
		// returns, and wants to re-select the same item... the 
		// ListSelectionListener as implemented only watches for changes in
		// the value of the JList to which it's associated.
		class ClipOwner implements ClipboardOwner
		{
			ClipOwner(String cipher) {		
				GnuPG gpg = new GnuPG();

				gpg.decrypt(cipher.toString(), AgentUI.passphrase);
				
				StringSelection result = new StringSelection(gpg.getResult());
				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
				clipboard.setContents(result, this);
			}

			public void lostOwnership(Clipboard clipboard, Transferable contents) {		
				// TO-DO: anything to do here?
			}
		}
		MouseListener mouseListener = new MouseListener() { 
	        public void mouseClicked(MouseEvent e) {
	        	Component c = (Component)e.getSource();	
				
				if (c instanceof JList) {
					
					// repaint & set wait cursor right away to soften the delay
					((JList) c).update(c.getGraphics());
					Cursor waitcursor = new Cursor(Cursor.WAIT_CURSOR);
					Cursor oldcursor = c.getCursor();
					c.getParent().setCursor(waitcursor);
					
					// get index of selection and grab matching index in password list
					int selection = (int) (((JList)e.getSource()).getSelectedIndex());
					String password = (String)AgentUIContent.pl[selection];
					
					// create a ClipOwner, which will push the password to the clipboard
					new ClipOwner(password);
					
					// done, restore old cursor
					c.getParent().setCursor(oldcursor);
				}
	        }
	        public void mouseEntered(MouseEvent e) {
			}
			public void mouseExited(MouseEvent e) {				
			}
			public void mousePressed(MouseEvent e) {				
			}
			public void mouseReleased(MouseEvent e) {				
			}
		};
		list.addMouseListener(mouseListener);
		
		// finally, add the list scrollpane to this JPanel.
		this.add(listScrollPane);
	}	
	
	// this is a listener class to be used by the JList.
	// any value change in the list triggers a gpg decryption
	// of the corresponding password blob, and the cleartext
	// is loaded into the clipboard.
	class CopyToClipboard implements ListSelectionListener 
	{
		class ClipOwner implements ClipboardOwner
		{
			ClipOwner(String cipher)
			{		
				GnuPG gpg = new GnuPG();

				gpg.decrypt(cipher.toString(), AgentUI.passphrase);
				
				StringSelection result = new StringSelection(gpg.getResult());
				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
				clipboard.setContents(result, this);
			}
			public void lostOwnership(Clipboard clipboard, Transferable contents) {		
				// TO-DO: anything to do here?
			}
		}
		public void valueChanged(ListSelectionEvent e)
		{
			Component c = (Component)e.getSource();	
				
			if (c instanceof JList && (e.getValueIsAdjusting())) {
				
				// repaint & set wait cursor right away to soften the delay
				((JList) c).update(c.getGraphics());
				Cursor waitcursor = new Cursor(Cursor.WAIT_CURSOR);
				Cursor oldcursor = c.getCursor();
				c.getParent().setCursor(waitcursor);
				
				// get index of selection and grab matching index in password list
				int selection = (int) (((JList)e.getSource()).getSelectedIndex());
				String password = (String)AgentUIContent.pl[selection];
				
				// create a ClipOwner, which will push the password to the clipboard
				new ClipOwner(password);
				
				// done, restore old cursor
				c.getParent().setCursor(oldcursor);
			}
		}
	}
}

